package com.allenyuan.gcode;

import java.awt.Color;
import java.awt.Composite;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.Image;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.RenderingHints.Key;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.FontRenderContext;
import java.awt.font.GlyphVector;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import java.awt.image.ImageObserver;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.renderable.RenderableImage;
import java.text.AttributedCharacterIterator;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public class GCodeGraphics2D extends Graphics2D {
	
	private List<String> gcodes = new ArrayList<String>();
	
	private Font font;
	
	private FontRenderContext fontRenderContext;
	
	public GCodeGraphics2D() {
		super();
		fontRenderContext = new FontRenderContext(getTransform(), false, false);
	}

	public List<String> getGcodes() {
		return gcodes;
	}

	@Override
	public void draw(Shape s) {
		// TODO Auto-generated method stub
		System.out.println("draw");
		PathIterator pit = s.getPathIterator(getTransform());
		double[] points = new double[6];
		int num = 0;
		Point2D current = null;
		while (!pit.isDone()) {
			num = pit.currentSegment(points);
			System.out.println(num);
			List<Point2D> waypoints = new ArrayList<Point2D>();
			switch (num) {
			case PathIterator.SEG_MOVETO:
				current = new Point2D.Double(points[0], points[1]);
				gcodes.add(GCodeGenerator.M05());
				gcodes.add(GCodeGenerator.G00(current));
				gcodes.add(GCodeGenerator.M03());
				break;
			case PathIterator.SEG_LINETO:
				gcodes.add(GCodeGenerator.G01(new Point2D.Double(points[0], points[1])));
				break;
			case PathIterator.SEG_QUADTO:
				waypoints = bezier2ToPoint2D(current,
						new Point2D.Double(points[0], points[1]),
						new Point2D.Double(points[2], points[3]));
				current = new Point2D.Double(points[2], points[3]);
				gcodes.addAll(GCodeGenerator.G01(waypoints));
				break;
			case PathIterator.SEG_CUBICTO:
				waypoints = bezier3ToPoint2D(current,
						new Point2D.Double(points[0], points[1]),
						new Point2D.Double(points[2], points[3]),
						new Point2D.Double(points[4], points[5]));
				current = new Point2D.Double(points[4], points[5]);
				gcodes.addAll(GCodeGenerator.G01(waypoints));
				break;
			case PathIterator.SEG_CLOSE:
				gcodes.add(GCodeGenerator.M05());
				break;
			default:
				break;
			}
			pit.next();
		}
	}

	@Override
	public boolean drawImage(Image img, AffineTransform xform, ImageObserver obs) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void drawImage(BufferedImage img, BufferedImageOp op, int x, int y) {
		// TODO Auto-generated method stub
		Raster raster = img.getData();
		System.out.println(img.getType());
		int height = raster.getHeight();
		int width = raster.getWidth();

		for (int i = 0; i < height; ++i) {
			Point2D end = new Point2D.Double(x, y + i);
			Point2D start = new Point2D.Double(x, y + i);
			// gcodes.add(GCodeGenerator.G00(start));
			boolean status = false;
			
			for (int j = 0; j < width; ++j) {
				int pixel = img.getRGB(j, i);
				
				raster.getPixel(0, 0, new int[1]);
				System.out.println("row:" + i + " column: " + j + " " + pixel);
				
				if (pixel == Color.BLACK.getRGB()) {
					System.out.println("black");
					if (status == false) {
						status = true;
						start.setLocation(x + j, y + i);
						gcodes.add(GCodeGenerator.G00(start));
					}
					else if (j == width - 1) {
						status = false;
						end.setLocation(x + j, y + i);
						gcodes.add(GCodeGenerator.G01(end));
					}
				}
				else if (pixel == Color.white.getRGB()){
					System.out.println("white");
					if (status == true) {
						status = false;
						end.setLocation(x + j, y + i);
						gcodes.add(GCodeGenerator.G01(end));
					}
				}
			}
		}
	}

	@Override
	public void drawRenderedImage(RenderedImage img, AffineTransform xform) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawRenderableImage(RenderableImage img, AffineTransform xform) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawString(String str, int x, int y) {
		// TODO Auto-generated method stub
		Font font = getFont();
		System.out.println(font.getName());
		GlyphVector gv = font.createGlyphVector(new FontRenderContext(null, false, false), str);
		Shape s = gv.getOutline(x, y);
		draw(s);
	}

	@Override
	public void drawString(String str, float x, float y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawString(AttributedCharacterIterator iterator, int x, int y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawString(AttributedCharacterIterator iterator, float x, float y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawGlyphVector(GlyphVector g, float x, float y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void fill(Shape s) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean hit(Rectangle rect, Shape s, boolean onStroke) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public GraphicsConfiguration getDeviceConfiguration() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setComposite(Composite comp) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setPaint(Paint paint) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setStroke(Stroke s) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setRenderingHint(Key hintKey, Object hintValue) {
		// TODO Auto-generated method stub

	}

	@Override
	public Object getRenderingHint(Key hintKey) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setRenderingHints(Map<?, ?> hints) {
		// TODO Auto-generated method stub

	}

	@Override
	public void addRenderingHints(Map<?, ?> hints) {
		// TODO Auto-generated method stub

	}

	@Override
	public RenderingHints getRenderingHints() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void translate(int x, int y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void translate(double tx, double ty) {
		// TODO Auto-generated method stub

	}

	@Override
	public void rotate(double theta) {
		// TODO Auto-generated method stub

	}

	@Override
	public void rotate(double theta, double x, double y) {
		// TODO Auto-generated method stub

	}

	@Override
	public void scale(double sx, double sy) {
		// TODO Auto-generated method stub

	}

	@Override
	public void shear(double shx, double shy) {
		// TODO Auto-generated method stub

	}

	@Override
	public void transform(AffineTransform Tx) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setTransform(AffineTransform Tx) {
		// TODO Auto-generated method stub

	}

	@Override
	public AffineTransform getTransform() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Paint getPaint() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Composite getComposite() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setBackground(Color color) {
		// TODO Auto-generated method stub

	}

	@Override
	public Color getBackground() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Stroke getStroke() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void clip(Shape s) {
		// TODO Auto-generated method stub

	}

	@Override
	public FontRenderContext getFontRenderContext() {
		// TODO Auto-generated method stub
		return fontRenderContext;
	}

	@Override
	public Graphics create() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Color getColor() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setColor(Color c) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setPaintMode() {
		// TODO Auto-generated method stub

	}

	@Override
	public void setXORMode(Color c1) {
		// TODO Auto-generated method stub

	}

	@Override
	public Font getFont() {
		// TODO Auto-generated method stub
		return font;
	}

	@Override
	public void setFont(Font font) {
		// TODO Auto-generated method stub
		this.font = font;
	}

	@Override
	public FontMetrics getFontMetrics(Font f) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Rectangle getClipBounds() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void clipRect(int x, int y, int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void setClip(int x, int y, int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public Shape getClip() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setClip(Shape clip) {
		// TODO Auto-generated method stub

	}

	@Override
	public void copyArea(int x, int y, int width, int height, int dx, int dy) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawLine(int x1, int y1, int x2, int y2) {
		// TODO Auto-generated method stub

	}

	@Override
	public void fillRect(int x, int y, int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void clearRect(int x, int y, int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
		// TODO Auto-generated method stub

	}

	@Override
	public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawOval(int x, int y, int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void fillOval(int x, int y, int width, int height) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
		// TODO Auto-generated method stub

	}

	@Override
	public void fillArc(int x, int y, int width, int height, int startAngle, int arcAngle) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawPolyline(int[] xPoints, int[] yPoints, int nPoints) {
		// TODO Auto-generated method stub

	}

	@Override
	public void drawPolygon(int[] xPoints, int[] yPoints, int nPoints) {
		// TODO Auto-generated method stub

	}

	@Override
	public void fillPolygon(int[] xPoints, int[] yPoints, int nPoints) {
		// TODO Auto-generated method stub

	}

	@Override
	public boolean drawImage(Image img, int x, int y, ImageObserver observer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
			ImageObserver observer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public boolean drawImage(Image img, int dx1, int dy1, int dx2, int dy2, int sx1, int sy1, int sx2, int sy2,
			Color bgcolor, ImageObserver observer) {
		// TODO Auto-generated method stub
		return false;
	}

	@Override
	public void dispose() {
		// TODO Auto-generated method stub

	}
	
	private static List<Point2D> bezier2ToPoint2D(Point2D p0, Point2D p1, Point2D p2) {
		double t;
		int npoints = (int)Math.abs((p0.getX() - p2.getX()));
		double delt = 1.0 / (double)(npoints);
		int i;
		t = 0.0;
		Point2D[] coeff = {p0, p1, p2};
		List<Point2D> points = new ArrayList<Point2D>();
		for (i = 0; i <= npoints; ++i) {
			points.add(decas(coeff, t, 2));
			t += delt;
		}
		return points;
	}
	
	private static List<Point2D> bezier3ToPoint2D(Point2D p0, Point2D p1, Point2D p2, Point2D p3) {
		double t;
		int npoints = (int)Math.abs((p0.getX() - p3.getX()));
		double delt = 1.0 / (double)(npoints);
		int i;
		t = 0.0;
		Point2D[] coeff = {p0, p1, p2, p3};
		List<Point2D> points = new ArrayList<Point2D>();
		for (i = 0; i <= npoints; ++i) {
			points.add(decas(coeff, t, 3));
			t += delt;
		}
		return points;
	}
	
	private static Point2D decas(Point2D[] coeff, double t, int degree) {
		double t1;
		Point2D[] coeffa = Arrays.copyOf(coeff, 10);
		t1 = 1.0 - t;
		
		for (int r = 1; r <= degree; ++r) {
			for (int i = 0; i <= degree - r; ++i) {
				Point2D p1 = new Point2D.Double(t1 * coeffa[i].getX(), t1 * coeffa[i].getY());
				Point2D p2 = new Point2D.Double(t * coeffa[i + 1].getX(), t * coeffa[i + 1].getY());
				coeffa[i] = new Point2D.Double(p1.getX() + p2.getX(), p1.getY() + p2.getY());
			}
		}
		return coeffa[0];
	}

}
